﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Collections;
using System.Reflection;

namespace Mighty.Common
{

    /// <summary>
    /// 게임 플레이 상태
    /// </summary>
    public enum GameMode
    {
        Server,
        Client,
        Offline,
        Tester
    }

    /// <summary>
    /// 각 플레이어에게 주어지는 상태
    /// </summary>
    public enum ActualStatus
    {
        GameStartEvent,             // (모두) 게임시작
        BettingReadyEvent,          // (모두) 베팅시작, 카드를 이미 나누어 주었다
        BettingTicEvent,            // (개인) 베팅차례
        BettingTalkEvent,           // (모두) 다른사람의 베팅 정보
        BettingCompleteEvent,       // (모두) 베팅완료, 주공이 정해졌다
        ChoiceCardEvent,            // (주공) 카드선택, 13장의 카드중 10장의 카드선택
        LastBettingEvent,           // (주공) 최종베팅정보, 기루다무늬변경가능
        LastBettingTalkEvent,       // (모두) 주공의 최종베팅정보
        ChoiceFriendEvent,          // (주공) 프랜드선택
        ChoiceFriendTalkEvent,      // (모두) 주공의 프랜드 정보
        RoundReadyEvent,            // (모두) 각라운드의시작
        RoundTicEvent,              // (각자) 카드낼차례
        RoundCardThrowEvent,        // (모두) 다른사람의 카드 정보
        RoundCompleteEvent,         // (모두) 각라운드의종료
        GameEndEvent                // (모두) 게임의 종료
    }


    /// <summary>
    /// 기루다무늬
    /// </summary>
    public enum Giruda
    {
        Clover,
        Diamond,
        Spade,
        Heart,
        NoGiruda
    }

    /// <summary>
    /// 카드무늬
    /// </summary>
    public enum Shape
    {
        Clover,
        Diamond,
        Spade,
        Heart,
        Joker
    }

    /// <summary>
    /// 플레이어
    /// </summary>
    public class Player : ICloneable
    {
        public bool isAI;                                           // 인공지능인지 player인지 구분
        public String name;                                         // player이름, 인공지능이름
        public int position;                                        // player자리위치 
        public List<RoundCard> pointCard;                           // 점수카드(RoundCard)
        public int port;
        public String ip;
        public String nick;
        public int score;

        public override String ToString()
        {
            String n = nick;
            if ( n == "" ) n = name;
            return String.Format("#{0} {1}", position, n);
        }
        public object Clone()
        {
            Player p = this.MemberwiseClone() as Player;
            if (pointCard != null)
            {
                p.pointCard = new List<RoundCard>();
                foreach (RoundCard rc in pointCard) p.pointCard.Add(rc.Clone() as RoundCard);
            }
            return p;
        }
    }

    /// <summary>
    /// 주공
    /// </summary>
    public class AttackPlayer : Player, ICloneable
    {
        public void SetPlayer(Player p)
        {
            isAI = p.isAI;
            name = p.name;
            position = p.position;
            pointCard = p.pointCard;
        }
        new public object Clone()
        {
            AttackPlayer a = this.MemberwiseClone() as AttackPlayer;
            return a;
        }
    }

    /// <summary>
    /// 프랜드. 만약 프랜드가 밝혀진다면 Player정보가 채워진다
    /// </summary>
    public class FriendPlayer : Player, ICloneable
    {
        public enum HowToSelect { card, player, firstwin, nofriend }
        public HowToSelect howToSelect;                     // 프랜드가 어떻게 선택되었는가
        public Player playerCondition;                      // 플레이어로 프랜드를 선택할 경우 조건
        public Card cardCondition;                          // 카드로 프랜드가 선택할 경우 조건
        public bool founded = false;

        public void SetPlayer(Player p)
        {
            isAI = p.isAI;
            name = p.name;
            position = p.position;
            pointCard = p.pointCard;
        }
        public String GetFriendCondition(GameInfo g)
        {
            String strFriend = "";
            if (howToSelect == FriendPlayer.HowToSelect.firstwin)
            {
                strFriend = String.Format("초구 프랜드", g.attackPlayer.position);
            }
            else if (howToSelect == FriendPlayer.HowToSelect.card)
            {
                if (g.friend.cardCondition.IsSame(g.mighty)) strFriend = "마이티 프랜드";
                else if (g.friend.cardCondition.shape == Shape.Joker) strFriend = "조커 프랜드";
                else if (g.friend.cardCondition.IsSame(g.jokerCall)) strFriend = "조커콜 프랜드";
                else strFriend = String.Format("{0} {1} 프랜드", cardCondition.shape, cardCondition.number);
            }
            else if (howToSelect == FriendPlayer.HowToSelect.nofriend)
            {
                strFriend = String.Format("노 프랜드", g.attackPlayer.position);
            }
            else if (howToSelect == FriendPlayer.HowToSelect.player)
            {
                strFriend = String.Format("{0} 프랜드", playerCondition.ToString());
            }
            return strFriend;
        }
        new public object Clone()
        {
            FriendPlayer f = this.MemberwiseClone() as FriendPlayer;    // Player의 Clone을 호출해 주지는 않는다
            if (pointCard != null)  
            {
                f.pointCard = new List<RoundCard>();
                foreach (RoundCard rc in pointCard) f.pointCard.Add(rc.Clone() as RoundCard);
            }
            if (playerCondition != null) f.playerCondition = playerCondition.Clone() as Player;
            if (cardCondition != null) f.cardCondition = cardCondition.Clone() as Card;
            return f;
        }
}

    /// <summary>
    /// 기본카드
    /// </summary>
    public class Card : ICloneable
    {
        public Shape shape;                                 // 무늬
        public int number;                                  // 숫자

        public Card() { }
        public Card(Shape s, int n)
        {
            shape = s;
            number = n;
        }
        public override String ToString()
        {
            return String.Format("{0} {1}", shape, number);
        }
        public bool IsSame(Card c)
        {
            if ( shape == c.shape && number == c.number ) return true;
            else return false;
        }
        public object Clone()
        {
            Card c = this.MemberwiseClone() as Card;
            return c;
        }
    }

    /// <summary>
    /// 손에 쥐고 있는 카드. RoundTicEvent이벤트는 HandedCard타입을 리턴한다. 조커콜카드를 내더라도
    /// cmdJokerCall를 true로 설정안하면 해당카드는 조커콜의 역활을 하지 않는다. 만약 조커를 제일처음 내면서
    /// 특정무늬를 뽑아내기를 원할 경우 cmdJokerTarget에 해당 무늬를 지정해야 한다
    /// </summary>
    public class HandedCard : Card, ICloneable
    {
        public bool isUsed;                                 // 이미 사용했는지 여부
        public bool isPossible;                             // 해당 라운드에서 사용 가능한지 여부
        public bool cmdRemove;                              // ChoiceCardEvent에서 버릴 카드여부 선택
        public bool cmdJokerCall;                           // 조커컬로 사용여부 선택
        public Shape cmdJokerTarget;                        // 조커가 호출하는 카드무늬

        public override String ToString()
        {
            string possible = "";
            if (!isPossible) possible = "  ";

            string used = "";
            if (isUsed) used = " used";

            return String.Format("{0}{1} {2}{3}", possible, shape, number, used);
        }
        new public object Clone()
        {
            HandedCard h = this.MemberwiseClone() as HandedCard;
            return h;
        }
    }

    /// <summary>
    /// 해당 라운드에서 사용된 카드. 각 플레이어는 
    /// </summary>
    public class RoundCard : Card, ICloneable
    {
        public int owner;                                   // 카드를 낸 사람
        public bool isJokerCall;                            // 조커컬로 사용되었는지에 대한 정보
        public Shape jokerTarget;                           // 조커가 어떤 무늬를 뽀아내었는지에 대한 정보

        public override String ToString()
        {
            return String.Format("#{0} {1} {2}", owner, shape, number);
        }
        new public object Clone()
        {
            RoundCard r = this.MemberwiseClone() as RoundCard;
            return r;
        }
    }

    /// <summary>
    /// 베팅. 각 플레이어의 베팅정보
    /// </summary>
    public class Betting : ICloneable
    {
        public bool isPass;                                 // 패스여부
        public Giruda giruda;                               // 기루다
        public int limit;                                   // 선언된 그림카드개수
        public int position;                                // 플레이어의위치
        
        public override String ToString()
        {
            string rtn;
            if (isPass) rtn = "pass";
            else rtn = String.Format("{0} {1}", giruda, limit);
            return rtn;
        }
        public object Clone()
        {
            Betting b = this.MemberwiseClone() as Betting;
            return b;
        }
    }

    /// <summary>
    /// 라운드. 게임은 총10라운드를 통해서 50장의 카드를 내게 된다
    /// </summary>
    public class Round : ICloneable
    {
        public RoundCard[] cardStack = new RoundCard[5];    // 해당라운드에서 사용한 카드
        public int winner;                                  // 해당라운드에서 이긴 player

        public object Clone()
        {
            Round r = this.MemberwiseClone() as Round;
            for (int i = 0; i < 5; i++) if (cardStack[i] != null) r.cardStack[i] = cardStack[i].Clone() as RoundCard;
            return r;
        }
    }

    /// <summary>
    /// 최종스코어
    /// </summary>
    public class Score : ICloneable
    {
        public int cntDependerCard;                         // 수비가먹은 그림카드개수
        public int baseScore;                               // 기본점수: x2적용후
        public int[] playerScore = new int[5];              // 플레이어별 점수

        public object Clone()
        {
            Score s = this.MemberwiseClone() as Score;
            return s;
        }
    }


    /// <summary>
    /// 게임정보. 각 플레이어가 가지게 되는 게임에 대한 정보
    /// </summary>
    public class GameInfo : ICloneable
    {
        public Player myInfo;                                       // 자신에 대한 정보
        public Player[] players = new Player[5];                    // 모든 플레이어 정보

        public HandedCard[] myCards = new HandedCard[13];           // 자기가 가지고 있는 카드에 대한정보
        public List<Betting> bettingHistory;                        // 모든 플레이어 베팅정보
        public Betting betting;                                     // 주공 베팅정보
        public AttackPlayer attackPlayer;                           // 주공
        public FriendPlayer friend;                                 // 프랜드

        public Card jokerCall;                                      // 조커콜 카드
        public Card mighty;                                         // 마이티 카드

        public Round[] roundHistory = new Round[10];                // 각 라운드
        public int roundIdx;                                        // 몇번째 라운드(총10라운드)
        public Round round;                                         // 현재 라운드
        public int roundCardIdx;                                    // 현재 라운드에 몇번째 카드(총5)

        public Score score;                                         // 최종스코어
        public int timeout;

        public object Clone()
        {
            GameInfo g = this.MemberwiseClone() as GameInfo;
            if (myInfo != null) g.myInfo = myInfo.Clone() as Player;
            for (int i = 0; i < 5; i++) if (players[i] != null) g.players[i] = players[i].Clone() as Player;

            for (int i = 0; i < 13; i++) if ( myCards[i] != null ) g.myCards[i] = myCards[i].Clone() as HandedCard;
            if (bettingHistory != null)
            {
                g.bettingHistory = new List<Betting>();
                foreach (Betting b in bettingHistory) g.bettingHistory.Add(b.Clone() as Betting);
            }
            if (betting != null) g.betting = betting.Clone() as Betting;
            if (attackPlayer != null) g.attackPlayer = attackPlayer.Clone() as AttackPlayer;
            if (friend != null) g.friend = friend.Clone() as FriendPlayer;

            if (jokerCall != null) g.jokerCall = jokerCall.Clone() as Card;
            if (mighty != null) g.mighty = mighty.Clone() as Card;

            for (int i = 0; i < 10; i++) if (roundHistory[i] != null) g.roundHistory[i] = roundHistory[i].Clone() as Round;
            if (roundIdx > -1 && roundIdx < 10) g.round = g.roundHistory[roundIdx];
            else g.round = null;
            if (score != null) g.score = score.Clone() as Score;
            
            return g;
        }
    }
}
